#pragma once
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Value.hpp>
#include <etcd/Watcher.hpp>
#include <string>
#include <memory>
#include "../spdlog/logger.hpp"
#include <unordered_map>

namespace ns_etcd_client
{
    // 服务注册
    class Register
    {
    public:
        Register(const std::string &etcd_url, const std::string &server_name, const std::string &server_address)
            : _client(make_unique<etcd::Client>(etcd_url)), _server_name(server_name), _server_address(server_address)
        {
            try
            {
                etcd::Response resp = _client->add(server_name, _server_address).get();
                if (!resp.is_ok())
                {
                    // 注册失败
                    LOG_WARN("服务注册失败:> {} ({}:{})", resp.error_message(), _server_name, server_address);
                }
                else
                {
                    // 注册成功
                    LOG_INFO("服务注册成功> {}:{}", _server_name, _server_address);
                }
            }
            catch (const std::exception &e)
            {
                LOG_ERROR("{}", e.what());
                exit(-1);
            }
        }

        ~Register()
        {
            // 下线服务
            if (_client->rm(_server_name).get().is_ok())
                LOG_INFO("下线服务> {}:{}", _server_name, _server_address);
        }

    private:
        std::unique_ptr<etcd::Client> _client;
        std::string _server_name;
        std::string _server_address;
    };

    // 服务发现
    class Discovery
    {
    private:
        using Func = function<void(const std::string server_name, const std::string server_address)>;

        void callback(std::unordered_map<std::string, std::string>& servers_map, const etcd::Response &resp)
        {
            if (!resp.is_ok())
            {
                LOG_WARN("{}", resp.error_message());
                return;
            }

            // Returns the action type of the operation that this response belongs to.
            etcd::Events events = resp.events();
            // using Events = std::vector<Event>;
            for (auto &e : events)
            {
                const std::string server_name = e.kv().key(), server_address = e.kv().as_string();
                const std::string prev_key = e.prev_kv().key(), prev_val = e.prev_kv().as_string();
                if (e.event_type() == etcd::Event::EventType::PUT)
                {
                    // 添加 or 更新
                    if (prev_val.empty())
                    {
                        //添加
                        servers_map.insert(make_pair(server_name, server_address));
                        LOG_INFO("发现新的服务:> {}:{}", server_name, server_address);
                    }
                    else
                    {
                        //更新
                        servers_map[server_name] = server_address;
                        LOG_INFO("更新服务:> {}:{} -> {}:{}", prev_key, prev_val, server_name, server_address);

                    }
                    _put(server_name, server_address);
                }
                if (e.event_type() == etcd::Event::EventType::DELETE_)
                {
                    // 服务下线
                    servers_map.erase(prev_key);
                    LOG_INFO("下线服务:> {}:{}", prev_key, prev_val);
                    _delete(prev_key, prev_val);
                }
            }
        }

        public:
            Discovery(const std::string &etcd_url, const std::string &servers, std::unordered_map<std::string, std::string>& servers_map, Func put, Func del, bool recursive = false)
                : _client(make_unique<etcd::Client>(etcd_url))
                , _put(put)
                , _delete(del)
            {
                servers_map = Get(servers);
                _watcher = make_unique<etcd::Watcher>(*_client, servers, std::bind(&Discovery::callback, this, servers_map, std::placeholders::_1), recursive);
            }

            // 获取发现的服务
            std::unordered_map<std::string, std::string> Get(const std::string& servers)
            {
                std::unordered_map<std::string, std::string> ret;
                try
                {
                    etcd::Response resp = _client->ls(servers).get();
                    if (!resp.is_ok())
                    {
                        LOG_WARN("服务发现失败:> {}", resp.error_message());
                        return ret;
                    }
                    int size = resp.keys().size();
                    for (int i = 0; i < size; ++i)
                    {
                        etcd::Value kv = resp.value(i);
                        ret.insert(make_pair(kv.key(), kv.as_string()));
                    }
                }
                catch (const std::exception &e)
                {
                    LOG_ERROR("{}", e.what());
                    exit(-1);
                }

                return ret;
            }

            ~Discovery()
            {
                _watcher->Wait();
                _watcher->Cancel();
            }

        private:
            std::unique_ptr<etcd::Client> _client;
            std::unique_ptr<etcd::Watcher> _watcher;

            // 如果键值发生改变，进行回调
            Func _put;
            Func _delete;
        };
    };